import random
from Tkinter import *
import math

class LogicNode():
    def __init__(self,id=0,value=0):
        self.value=value
        self.id=id
        self.dendrites={}
        self.input_queue=[]
        
        node_indices = range(0,5)
        
        
        dend_indices = random.sample(xrange(20), 5)
        print dend_indices
        
        for node_idx in dend_indices:
            if node_idx!=self.id:
                str = random.gauss(1,.05)
                dend_idx = random.randint(0,20)
                self.dendrites[node_idx]=str
        print self.dendrites
        
        
    def connections(self):
        return self.dendrites
    
    def queueInput(self,value):
        self.input_queue.append(value)
        
    def commitQueue(self):
        """
        commit the 'axon' queue by summing the input values.
        """
        for each in self.input_queue:
            self.value+=each
        self.input_queue = []
            
    def propagateSignal(self):
        """
        Nieve implementation: divide the current value by the number of dendrites and 
        multiply it by the signal strength. Queue the impulse onto the target dendrite node. 
        """
        for each in self.input_queue:
            self.value+=each
    
    def __str__(self):
        tmp='Value: %(num)g'%{'num':self.value}+'\n'
        #tmp+='Conns: '+str(self.connections)+'\n'
        tmp+='queue: '+str(self.input_queue)+'\n'
        return tmp

class LogicalUnit():
    

    def __init__(self,size=5,mean=5,std=1):
        self.nodes={}
        self.size=size
        
        
        for i in range(0,self.size):
            self.nodes[i]=LogicNode(i,random.gauss(mean,std))
            
        
        #label the nodes which handle external stimuli
        self.input_indices=[1,3,4]
    
    
    
    
    def getConnections(self):
        """
        Change this to where you add a node?
        """
        conns = {}
        drawables = []
        
        for src_idx,src_node in self.nodes.iteritems():
            
            for dest_idx,conn_strength in src_node.dendrites.iteritems():
                 print src_idx,dest_idx
                #Make sure we aren't double-connecting
        #        if conns.get(dest_idx,{}).get(src_idx,None) != None:
                 #if src_idx in conns and dest_idx not in conns[src_idx]:
                 if src_idx not in conns:
                     conns[src_idx] = {}
                 conns[src_idx][dest_idx] = conn_strength
                 is_input = False
                 if src_idx in self.input_indices:
                     is_input = True
                     drawables.append( {'src':src_idx,'dest':dest_idx,'str':conn_strength, 'is_input':is_input} )
                                     
                 
        return drawables
    
    def __str__(self):
        tmp=''
        for each,val in self.nodes.iteritems():
            tmp+=str(val)+'\n'
        return str(tmp)


    def exciteUnit(self,iv):
        """
        The keys in the Information vector specify target nodes in the logical unit. The input queue in those target nodes
        are appended the corresponding value in the InformationVector.
        """
        
        for node_index in self.input_nodes:
            self.nodes[node_index].queueInput(iv.get(node_index))
            
    def commmitStimulus(self):
        """
        For each of the nodes, send the message to commit the queue. This starts the 'pulse' of the signal propagation.
        """
        for key,node in self.nodes.iteritems():
            node.commitQueue()

        for key,node in self.nodes.iteritems():
            node.propagateSignal()
    

class InformationVector():
    """Vector of information which also contain a mapping
    to specified nodes in a LogicalUnit."""
    
    def __init__(self,num_vals=10):
        self.n=num_vals
        self.data={}
        self.randomFill()
    
    def get(self,node_index):
        return self.data.get(node_index,0)
    
    def randomFill(self,mean=1,std=.5):
        for i in range(1,self.n):
            self.data[i]=random.gauss(mean,std)
        
    def __str__(self):
        return str(self.n) + ' values:\n' + str(self.data.values())
    





class LUGui():
    def __init__(self, master, logical_unit = None):
        self.circle_points={}
        self.__ensureCircleSize(20)
        self.logical_unit= logical_unit
        frame = Frame(master)
        frame.pack()

        self.w = Canvas(master, width=600, height=600)
        self.update()
        self.w.pack()
        
    
    def __ensureCircleSize(self,size):
        scale = 100
        xtrans  = 200
        ytrans = 200
        if size > len(self.circle_points):
            self.circle_points = {}
            num_pts = size
            for idx in range(0,num_pts,1):
                val = (idx*2*math.pi)/(num_pts+0.0)
                self.circle_points[idx] = {'x':math.cos(val)*scale+xtrans,'y':math.sin(val)*scale + ytrans}
        
    
    
        
    def update(self):
        connections = self.logical_unit.getConnections()
        num = len(connections)
        
        
        for index,point in self.circle_points.iteritems():
            self.w.create_oval(point['x'],point['y'],point['x']+5,point['y']+5, fill = 'blue')
        
        print connections
        for conn in connections:
            src_pt = self.circle_points[conn['src']]
            dest_pt = self.circle_points[conn['dest']]
            intensity = conn['str']*256*256*256*16
            color = "#%02d%02d%02d" % (intensity, intensity,intensity)
            print color
            self.w.create_line(src_pt['x'],src_pt['y'],dest_pt['x'],dest_pt['y'],fill=color)
        
        
        
        
        
        #for idx in range(0,len(points)):
        #    circlepoints[idx] = (sin())
        
        
        
            
        
        
        
        #w.create_line(point[0], 100, 200, 0, fill="red", dash=(4, 4))
        
        #w.create_rectangle(50, 25, 150, 75, fill="blue")
            
        

#    def say_hi(self):
#        print "hi there, everyone!"


#lu.exciteUnit(iv)
#print lu
#lu.exciteUnit(iv)
#lu.commmitStimulus()
#print '==============='
#print lu

iv=InformationVector()
lu=LogicalUnit()
root = Tk()

app = LUGui(root, lu)
#app.update()

root.mainloop()

